<?php

namespace App\Http\Controllers\Wechat;

use App\Models\RegisterVerify;
use App\Models\UserAppletInfo;
use App\Models\UserInfo;
use App\Models\UserSystemUnit;
use App\Models\UserWechatInfo;
use App\Validate\WechatAuthValidate;
use Exception;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use WXBizDataCrypt;

/**
 * 微信小程序授权登录   wx端
 * Class auth
 * @package app\Port\controller
 */
class AppletAuthController extends CommonController
{
    private $applet_appid;
    private $applet_appsecret;


    public $model = null;
    public $validate = null;

    public function __construct()
    {
        parent::__construct();
        $this->applet_appid = config('other.applet_appid');
        $this->applet_appsecret = config('other.applet_appsecret');

        $this->model = new UserAppletInfo();
        $this->validate = new WechatAuthValidate();
    }

    /**
     * 微信确认授权后登录（小程序使用）
     * @param code  微信code参数
     * @param invite  邀请码
     * @param nickname 用户昵称
     * @param head_img 用户头像  （新注册微信，无头像）
     */
    public function appletIndexInfo()
    {
        //增加验证场景进行验证
        if (!$this->validate->scene('wx_applet_auth')->check($this->request->all())) {
            return $this->returnApi(201,  $this->validate->getError());
        }

        $code = $this->request->code;/*code参数*/
        $invite = $this->request->invite;/*code参数*/
        $nickname = $this->request->nickname;
        $head_img = $this->request->head_img;
        if (empty($nickname)) {
            $userWechatModel = new UserWechatInfo();
            $nickname = $userWechatModel->getNickname();
        }
        if (empty($head_img)) {
            $head_img = $this->getImgAddrUrl() . 'default/default_head_img.png';
        }

        $arr = $this->getSmallOpenId($code);
        if (empty($arr['open_id'])) {
            return $this->returnApi(202, '授权失败，请联系管理员处理！');
        }
        $session_key = $arr['session_key'];
        $openid = $arr['open_id'];
        $unionid = !empty($arr['unionid']) ? $arr['unionid'] : null;


        // 启动事务
        DB::beginTransaction();
        try {
            /*检查是否已经绑定过账号*/
            $wechat = $this->model->change([
                'nickname' => $nickname,
                'head_img' => $head_img,
                'session_key' => $session_key,
                'open_id' => $openid,
                'union_id' => $unionid,
            ]);
            if (empty($wechat['token'])) {
                throw new Exception('登录失败');
            }

            $userInfoModel = new UserInfo();
            //邀请人加积分
            if (config('other.is_need_score') && $wechat['is_add'] && !empty($invite)) {
                /**执行积分规则 */
                //增加邀请人的积分

                $invite_user_info = $userInfoModel->where('qr_code', $invite)->first();
                if (!empty($invite_user_info['account_id'])) {
                    //添加邀请人数
                    $invite_user_info->invite_number = ++$invite_user_info->invite_number;
                    $invite_user_info->save();

                    $scoreRuleObj = new \App\Http\Controllers\ScoreRuleController();
                    $score_status = $scoreRuleObj->checkScoreStatus(2, $invite_user_info['id'], $invite_user_info['account_id']);

                    if ($score_status['code'] == 200) {
                        $system_id = $this->systemAdd($score_status['score_info']['type_name'], $invite_user_info['id'], $invite_user_info['account_id'], 11, 0, $score_status['score_info']['intro']);

                        $scoreRuleObj->scoreChange($score_status, $invite_user_info['id'], $invite_user_info['account_id'], $system_id); //添加积分消息
                    }
                }

                //写入用户信息表
                $userInfoModel->where('applet_id', $wechat['applet_id'])->update([
                    'invite_user_id' => !empty($invite_user_info['id']) ? $invite_user_info['id'] : 0,
                    'invite_account_id' => !empty($invite_user_info['account_id']) ? $invite_user_info['account_id'] : 0,
                ]);
            }

            // $account_info = $userInfoModel->getAccountInfo($wechat['token']);
            // if ($account_info) {
            //     $account = $account_info['account'];
            // } else {
            //     $account = null;
            // }

            DB::commit();
            return $this->returnApi(200, "授权成功", true, ['token' => $wechat['token'], 'qr_code' => $wechat['qr_code'], 'head_img' => $wechat['head_img'], 'nickname' => $wechat['nickname']]);
        } catch (\Exception $e) {
            Log::error('授权信息保存失败' . $e->getMessage() . $e->getFile() . $e->getLine());
            // 回滚事务
            DB::rollBack();
            return $this->returnApi(202, '授权信息保存失败');
        }
    }


    /**
     * 微信小程序授权获取电话号码后，立即登录
     * @param code  微信code参数  获取open_id 的code
     * @param invite  邀请码
     * @param phone_code      获得电话号码的code
     * @param nickname 用户昵称
     * @param head_img 用户头像  （新注册微信，无头像）
     * @return mixed
     */
    public function appletTelIndexInfo()
    {
        //增加验证场景进行验证
        if (!$this->validate->scene('wx_applet_tel_auth')->check($this->request->all())) {
            return $this->returnApi(201,  $this->validate->getError());
        }
        //  Log::error($this->request->all());

        $code = $this->request->code;/*code参数*/
        $invite = $this->request->invite;/*code参数*/
        $nickname = $this->request->nickname;
        $head_img = $this->request->head_img;
        $phone_code = $this->request->phone_code;
        if (empty($nickname)) {
            $userWechatModel = new UserWechatInfo();
            $nickname = $userWechatModel->getNickname();
        }
        if (empty($head_img)) {
            $head_img = $this->getImgAddrUrl() . 'default/default_head_img.png';
        }
        $arr = $this->getSmallOpenId($code);

        if (empty($arr['open_id'])) {
            return $this->returnApi(202, '授权失败，请联系管理员处理！');
        }
        $session_key = $arr['session_key'];
        $open_id = $arr['open_id'];
        $unionid = !empty($arr['unionid']) ? $arr['unionid'] : null;

        $phone_info = $this->getAppletTel($phone_code);
        if (empty($phone_info['tel'])) {
            return $this->returnApi(202, '电话号码获取失败，请稍后重试！');
        } else {
            $tel = $phone_info['tel'];
        }
        // 启动事务
        DB::beginTransaction();
        try {
            /*检查是否已经绑定过账号*/
            $wechat = $this->model->changeByTel([
                'tel' => $tel,
                'nickname' => $nickname,
                'head_img' => $head_img,
                'session_key' => $session_key,
                'open_id' => $open_id,
                'union_id' => $unionid,
                'invite' => $invite,
            ]);
            if (empty($wechat['token'])) {
                throw new Exception('登录失败');
            }

            //邀请人加积分
            if (config('other.is_need_score') && $wechat['is_add'] && !empty($invite)) {
                /**执行积分规则 */
                //增加邀请人的积分
                $userInfoModel = new UserInfo();
                $invite_user_info = $userInfoModel->where('qr_code', $invite)->first();
                if (!empty($invite_user_info)) {
                    $scoreRuleObj = new \App\Http\Controllers\ScoreRuleController();
                    $score_status = $scoreRuleObj->checkScoreStatus(2, $invite_user_info['id']);

                    if ($score_status['code'] == 200) {
                        $system_id = $this->systemAdd($score_status['score_info']['type_name'], $invite_user_info['id'], $invite_user_info['id'], 10, 0, $score_status['score_info']['intro']);

                        $scoreRuleObj->scoreChange($score_status, $invite_user_info['id'], null, $system_id); //添加积分消息，邀请人
                    }

                    //写入邀请用户信息表
                    $invite_user_info->invite_number = $invite_user_info['invite_number'] + 1; //邀请用户，邀请数量
                    $invite_user_info->save(); //邀请用户，邀请数量

                    //写入被邀请用户的用户信息表
                    $userInfoModel->where('id', $wechat['user_id'])->update([
                        'invite_score' => !empty($score_status['score_info']['score']) ? $score_status['score_info']['score'] : 0, //邀请者获得的积分数量
                        'invite_user_id' => !empty($invite_user_info['id']) ? $invite_user_info['id'] : 0, //邀请者的用户id
                        'invite_account_id' => !empty($invite_user_info['account_id']) ? $invite_user_info['account_id'] : 0, //邀请者的
                        'invite_applet_id' => !empty($invite_user_info['applet_id']) ? $invite_user_info['applet_id'] : 0, //邀请者的
                        'invite_wechat_id' => !empty($invite_user_info['wechat_id']) ? $invite_user_info['wechat_id'] : 0, //邀请者的
                    ]);
                }
            }

            DB::commit();
            return $this->returnApi(200, "授权成功", true, [
                'token' => $wechat['token'],
                'qr_code' => $wechat['qr_code'],
                'tel' => $tel,
                'head_img' => $wechat['head_img'],
                'nickname' => $wechat['nickname'],
                'serial_number' => $wechat['serial_number'],
            ]);
        } catch (\Exception $e) {
            Log::error('授权信息保存失败' . $e->getMessage() . $e->getFile() . $e->getLine());
            // 回滚事务
            DB::rollBack();
            return $this->returnApi(202, '授权信息保存失败');
        }
    }


    /**
     * 根据code获取小程序用户 openid   （内部调用）
     * @param code
     **/
    public function getSmallOpenId($code)
    {
        if (empty($code)) {
            return false;
        }
        //小程序授权地址
        $json = request_url('https://api.weixin.qq.com/sns/jscode2session?appid=' . $this->applet_appid . '&secret=' . $this->applet_appsecret . '&js_code=' . $code . '&grant_type=authorization_code');
        $arr = json_decode($json, true); //转化为数组
        if (isset($arr['openid'])) {
            $openid = $arr['openid'];
            $session_key = $arr['session_key'];
            $unionid = !empty($arr['unionid']) ? $arr['unionid'] : null;
            return ['open_id' => $openid, 'session_key' => $session_key, 'unionid' => $unionid];
        } else {
            Log::error('授权失败：' . $json);
        }
        return false;
    }

    /**
     * 根据code获取小程序用户 手机号码   （内部调用）
     * @param code
     **/
    public function getAppletTel($code, $number = 1)
    {
        if (empty($code)) {
            return false;
        }
        $access_token = $this->getWxAppletAccessToken();
        //小程序授权地址
        $url = 'https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=' . $access_token;
        $json = request_url($url, json_encode(['code' => $code]));
        $arr = json_decode($json, true); //转化为数组
        if (isset($arr['phone_info'])) {
            $tel = $arr['phone_info']['purePhoneNumber'];
            $phoneNumber = $arr['phone_info']['phoneNumber'];
            return ['tel' => $tel, 'phoneNumber' => $phoneNumber];
        } else {
            if ($number == 1) {
                //解决线下和线上代码冲突问题,重复获取access_token
                if (stripos($arr['errmsg'], 'access_token is invalid or not latest') !== false) {
                    $key = 'wx_applet_access_token';
                    Cache::forget($key); //清除缓存
                    return $this->getAppletTel($code, 2);
                }
            }
            Log::error($number . '-获取用户手机号码失败：' . $json);
        }
        return false;
    }

    /**
     * 获取小程序 access_token
     * @param $contractor YXYZ
     */
    public function getWxAppletAccessToken()
    {
        $key = 'wx_applet_access_token';
        $access_token = Cache::get($key);

        //获取appid  和 appsecret
        if (empty($access_token)) {
            $url = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=' . $this->applet_appid . '&secret=' . $this->applet_appsecret;
            $json = request_url($url);
            $res = json_decode($json, true);

            if (!isset($res['access_token'])) {
                Log::error('获取access_token失败：' . $json);
            }

            Cache::put($key, $res['access_token'], $res['expires_in']);

            $access_token = $res['access_token'];
        }
        return $access_token;
    }


    /**
     * 退出微信小程序登录 (公众号禁止调用此接口)
     * @param token  用户token
     */
    public function appletLogout()
    {
        $user_id = $this->request->user_info['id'];

        $userInfoModel = new UserInfo();
        $user_info = $userInfoModel->where('id', $user_id)->first();
        if (empty($user_info['applet_id'])) {
            return $this->returnApi(200, '退出成功', true);
        }

        DB::beginTransaction();
        try {
            $userAppletInfoModel = new UserAppletInfo();
            $user_applet_info = $userAppletInfoModel->where('id', $user_info['applet_id'])->first();
            $user_applet_info->old_open_id = $user_applet_info->open_id;
            $user_applet_info->open_id = null;
            $user_applet_info->session_key = null;
            $user_applet_info->union_id = null;
            $user_applet_info->nickname = null;
            $user_applet_info->head_img = null;
            $user_applet_info->save();

            //去掉用户表的 微信登录信息
            $user_info->applet_id = null;
            $user_info->save();

            DB::commit();
            return $this->returnApi(200, "退出成功", true);
        } catch (\Exception $e) {
            DB::rollBack();
            return $this->returnApi(202, $e->getMessage() . $e->getFile() . $e->getLine());
        }
    }

    /**
     * 获取微信推广二维码（带用户信息）
     * @param save_addr 保存地址，如果存在，直接保存，不存在直接 base64数据
     * @param number 请求次数
     */
    function getAppletQrCode($qr_code, $save_addr = '', $number = 1)
    {
        //获取accessToken
        $access_token = $this->getWxAppletAccessToken();
        $data = [];
        //配置参数
        $data['scene'] = $qr_code; //前台扫码后获取的数据
        $data['width'] = 300; //二维码大小比例
        $data['page'] = 'pages/index/index'; //二维码大小比例
        $data['check_path'] = false; //二维码大小比例
        $data['env_version'] = 'release'; //要打开的小程序版本。正式版为 "release"，体验版为 "trial"，开发版为 "develop"。默认是正式版。
        $url = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=" . $access_token;
        $data = json_encode($data);
        $result = $this->curlPost($url, $data);
        
        //解决线下和线上代码冲突问题,重复获取access_token
        if ($number == 1 && stripos($result, 'access_token is invalid or not latest') !== false) {
            $key = 'wx_applet_access_token';
            Cache::forget($key); //清除缓存
            $this->getAppletQrCode($qr_code, $save_addr = '', 2);
        }

        if (stripos($result, 'errcode') !== false) {
            //如果成功是直接返回图片，不成功是返回错误信息
            Log::error('getAppletQrCode error:' . $result);
            return false;
        }
        if ($save_addr) {
            //删除过期的临时文件
            $filedir = public_path('uploads') . '/temp_qr/';
            del_past_imgs($filedir, 7200); //删除过期图片

            if (!file_exists(dirname($save_addr))) {
                mkdir(dirname($save_addr), 0777, true); //true  表示递归创建
            }
            //生成二维码
            return file_put_contents($save_addr, $result);
        } else {
            $result = $this->data_to_base64($result, 'image/png');
            return $result;
        }
    }
    //转换成base64位格式
    function data_to_base64($contents, $mime)
    {
        $base64   = base64_encode($contents);
        return ('data:' . $mime . ';base64,' . $base64);
    }
    function curlPost($url, $data)
    {
        $ch = curl_init();
        //  $header = "Accept-Charset: utf-8";
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
        //  curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
        curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (compatible; MSIE 5.01; Windows NT 5.0)');
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
        curl_setopt($ch, CURLOPT_AUTOREFERER, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        $tmpInfo = curl_exec($ch);
        if (curl_errno($ch)) {
            return false;
        } else {
            return $tmpInfo;
        }
    }
}
